/**
 * $Id: ghostwinlay.c 19823 2009-04-20 18:23:46Z ton $
 *
 * ***** BEGIN GPL LICENSE BLOCK *****
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
 * All rights reserved.
 *
 * The Original Code is: all of this file.
 *
 * Contributor(s): none yet.
 *
 * ***** END GPL LICENSE BLOCK *****
 */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "MEM_guardedalloc.h"

#include "DNA_listBase.h"	
#include "DNA_userdef_types.h"	/* U.flag & TWOBUTTONMOUSE */

#include "BLI_blenlib.h"

#include "GHOST_C-api.h"

#include "BKE_utildefines.h"
#include "BKE_global.h"

#include "BIF_gl.h"
#include "BIF_graphics.h"
#include "BIF_mywindow.h"
#include "BIF_screen.h"
#include "BIF_usiblender.h"
#include "BIF_cursors.h"

#include "PIL_dynlib.h"

#include "mydevice.h"
#include "blendef.h"

#include "winlay.h"

#include <math.h>


#ifdef __APPLE__
#include <OpenGL/OpenGL.h>
#define __CARBONSOUND__
  /* XXX BIG WARNING: carbon.h should not be included in blender/src code, it conflicts with struct ID */
#define ID ID_
#include <Carbon/Carbon.h>

/*declarations*/
int checkAppleVideoCard(void);
void getMacAvailableBounds(short *, short *, short *, short *);

#endif
///

struct _Window {
	GHOST_WindowHandle	ghostwin;
	
	/* Handler and private data for handler */
	WindowHandlerFP		handler;
	void				*user_data;
	
	/* Window state */
	int		size[2], position[2];
	int		active, visible;
	
	/* Last known mouse/button/qualifier state */
	int		lmouse[2];
	int		lqual;		/* (LR_SHFTKEY, LR_CTRLKEY, LR_ALTKEY, LR_COMMANDKEY) */
	int		lmbut;		/* (L_MOUSE, M_MOUSE, R_MOUSE) */

	/* xtilt and ytilt represent how much the pen is tilted away from 
	 * vertically upright in either the X or Y direction, with X and Y the
	 * axes of the tablet surface.
	 * In other words, Xtilt and Ytilt are components of a vector created by projecting
	 * the pen's angle in 3D space vertically downwards on to the XY plane
	 * --Matt
	 */
	float pressure;			/* tablet pressure - 0.0 (no pressure) to 1.0 (full pressure) */
							/* mouse clicks and non-contacting stylus buttons generate pressure of 0.0. */
	float xtilt, ytilt;		/* tablet tilt value - x and y components of 3D angle 
							 * ranging from 0.0 (pen upright) to 1.0 (pen fully leaning over) */
	short activedevice;		/* Active input device currently in use (DEV_MOUSE, DEV_STYLUS, DEV_ERASER) */
	
	
	/* Tracks the faked mouse button, if non-zero it is
	 * the event number of the last faked button.
	 */
	int		faked_mbut;

		/* Last known ndof device state
         * note that the ghost device manager 
         * can handle any number of devices, but ghostwinlay can't
         */
    float   ndof[7];    /* tx, ty, tz, rx, ry, rz, dt */

	GHOST_TimerTaskHandle	timer;
	int						timer_event;
};

///

#ifdef __APPLE__

/* to avoid killing small end comps, we want to allow
   blender to start maximised if all the followings are true :
		- Renderer is OpenGL capable
		- Hardware acceleration
		- VRAM > 16 Mo
		
   we will bail out if VRAM is less than 8Mo
		*/
		
static int macPrefState = 0;
		
int checkAppleVideoCard(void) {
	long theErr;
	unsigned long display_mask;
	CGLRendererInfoObj rend;
	long nrend;
	int j;
	long value;
	long maxvram = 0;   /* we get always more than 1 renderer, check one, at least, has 8 Mo */
	
	display_mask = CGDisplayIDToOpenGLDisplayMask (CGMainDisplayID() );	
	
	theErr = CGLQueryRendererInfo( display_mask, &rend, &nrend);
	if (theErr == 0) {
		theErr = CGLDescribeRenderer (rend, 0, kCGLRPRendererCount, &nrend);
		if (theErr == 0) {
			for (j = 0; j < nrend; j++) {
				theErr = CGLDescribeRenderer (rend, j, kCGLRPVideoMemory, &value); 
				if (value > maxvram)
					maxvram = value;
				if ((theErr == 0) && (value >= 20000000)) {
					theErr = CGLDescribeRenderer (rend, j, kCGLRPAccelerated, &value); 
					if ((theErr == 0) && (value != 0)) {
						theErr = CGLDescribeRenderer (rend, j, kCGLRPCompliant, &value); 
						if ((theErr == 0) && (value != 0)) {
							/*fprintf(stderr,"make it big\n");*/
							CGLDestroyRendererInfo (rend);
							macPrefState = 8;
							return 1;
						}
					}
				}
			}
		}
	}
	if (maxvram < 7500000 ) {       /* put a standard alert and quit*/ 
		SInt16 junkHit;
		char  inError[] = "* Not enough VRAM    ";
		char  inText[] = "* blender needs at least 8Mb    ";
		inError[0] = 16;
		inText[0] = 28;
				
		fprintf(stderr, " vram is %li . not enough, aborting\n", maxvram);
		StandardAlert (   kAlertStopAlert, (ConstStr255Param) &inError, (ConstStr255Param)&inText,NULL,&junkHit);
		abort();
	}
	CGLDestroyRendererInfo (rend);
	return 0;
}

void getMacAvailableBounds(short *top, short *left, short *bottom, short *right) {
	Rect outAvailableRect;
	
	GetAvailableWindowPositioningBounds ( GetMainDevice(), &outAvailableRect);
	
	*top = outAvailableRect.top;  
    *left = outAvailableRect.left;
    *bottom = outAvailableRect.bottom; 
    *right = outAvailableRect.right;
}

#endif


static GHOST_SystemHandle g_system= 0;

	/* Some simple ghost <-> blender conversions */
	
static GHOST_TStandardCursor convert_cursor(int curs) {
	switch(curs) {
	default:
	case CURSOR_STD:		return GHOST_kStandardCursorDefault;
	case CURSOR_VPAINT:		return GHOST_kStandardCursorRightArrow;
	case CURSOR_FACESEL:		return GHOST_kStandardCursorRightArrow;
	case CURSOR_WAIT:		return GHOST_kStandardCursorWait;
	case CURSOR_EDIT:		return GHOST_kStandardCursorCrosshair;
	case CURSOR_HELP:		
#ifdef __APPLE__
		return GHOST_kStandardCursorLeftRight;
#else
		return GHOST_kStandardCursorHelp;
#endif
	case CURSOR_X_MOVE:		return GHOST_kStandardCursorLeftRight;
	case CURSOR_Y_MOVE:		return GHOST_kStandardCursorUpDown;
	case CURSOR_PENCIL:		return GHOST_kStandardCursorPencil;
	}
}

static int convert_mbut(GHOST_TButtonMask but) {
	if (but == GHOST_kButtonMaskLeft) {
		return LEFTMOUSE;
	} else if (but == GHOST_kButtonMaskRight) {
		return RIGHTMOUSE;
	} else {
		return MIDDLEMOUSE;
	}
}

static int convert_key(GHOST_TKey key) {
	if (key>=GHOST_kKeyA && key<=GHOST_kKeyZ) {
		return (AKEY + ((int) key - GHOST_kKeyA));
	} else if (key>=GHOST_kKey0 && key<=GHOST_kKey9) {
		return (ZEROKEY + ((int) key - GHOST_kKey0));
	} else if (key>=GHOST_kKeyNumpad0 && key<=GHOST_kKeyNumpad9) {
		return (PAD0 + ((int) key - GHOST_kKeyNumpad0));
	} else if (key>=GHOST_kKeyF1 && key<=GHOST_kKeyF12) {
		return (F1KEY + ((int) key - GHOST_kKeyF1));
	} else {
		switch (key) {
		case GHOST_kKeyBackSpace:		return BACKSPACEKEY;
		case GHOST_kKeyTab:				return TABKEY;
		case GHOST_kKeyLinefeed:		return LINEFEEDKEY;
		case GHOST_kKeyClear:			return 0;
		case GHOST_kKeyEnter:			return RETKEY;
	
		case GHOST_kKeyEsc:				return ESCKEY;
		case GHOST_kKeySpace:			return SPACEKEY;
		case GHOST_kKeyQuote:			return QUOTEKEY;
		case GHOST_kKeyComma:			return COMMAKEY;
		case GHOST_kKeyMinus:			return MINUSKEY;
		case GHOST_kKeyPeriod:			return PERIODKEY;
		case GHOST_kKeySlash:			return SLASHKEY;

		case GHOST_kKeySemicolon:		return SEMICOLONKEY;
		case GHOST_kKeyEqual:			return EQUALKEY;

		case GHOST_kKeyLeftBracket:		return LEFTBRACKETKEY;
		case GHOST_kKeyRightBracket:	return RIGHTBRACKETKEY;
		case GHOST_kKeyBackslash:		return BACKSLASHKEY;
		case GHOST_kKeyAccentGrave:		return ACCENTGRAVEKEY;

		case GHOST_kKeyLeftShift:		return LEFTSHIFTKEY;
		case GHOST_kKeyRightShift:		return RIGHTSHIFTKEY;
		case GHOST_kKeyLeftControl:		return LEFTCTRLKEY;
		case GHOST_kKeyRightControl:	return RIGHTCTRLKEY;
		case GHOST_kKeyCommand:			return COMMANDKEY;
		case GHOST_kKeyLeftAlt:			return LEFTALTKEY;
		case GHOST_kKeyRightAlt:		return RIGHTALTKEY;

		case GHOST_kKeyCapsLock:		return CAPSLOCKKEY;
		case GHOST_kKeyNumLock:			return 0;
		case GHOST_kKeyScrollLock:		return 0;

		case GHOST_kKeyLeftArrow:		return LEFTARROWKEY;
		case GHOST_kKeyRightArrow:		return RIGHTARROWKEY;
		case GHOST_kKeyUpArrow:			return UPARROWKEY;
		case GHOST_kKeyDownArrow:		return DOWNARROWKEY;

		case GHOST_kKeyPrintScreen:		return 0;
		case GHOST_kKeyPause:			return PAUSEKEY;

		case GHOST_kKeyInsert:			return INSERTKEY;
		case GHOST_kKeyDelete:			return DELKEY;
		case GHOST_kKeyHome:			return HOMEKEY;
		case GHOST_kKeyEnd:				return ENDKEY;
		case GHOST_kKeyUpPage:			return PAGEUPKEY;
		case GHOST_kKeyDownPage:		return PAGEDOWNKEY;

		case GHOST_kKeyNumpadPeriod:	return PADPERIOD;
		case GHOST_kKeyNumpadEnter:		return PADENTER;
		case GHOST_kKeyNumpadPlus:		return PADPLUSKEY;
		case GHOST_kKeyNumpadMinus:		return PADMINUS;
		case GHOST_kKeyNumpadAsterisk:	return PADASTERKEY;
		case GHOST_kKeyNumpadSlash:		return PADSLASHKEY;

		case GHOST_kKeyGrLess:		    return GRLESSKEY; 
			
		case GHOST_kKeyUnknown:			return UNKNOWNKEY;

		default:
			return 0;
		}
	}
}

	/***/
	

static Window *window_new(GHOST_WindowHandle ghostwin)
{
	Window *win= MEM_callocN(sizeof(*win), "Window");
	win->ghostwin= ghostwin;
	
	return win;
}

static void window_handle(Window *win, short event, short val)
{
	if (win->handler) {
		win->handler(win, win->user_data, event, val, 0);
	}
}

static void window_handle_ext(Window *win, short event, short val, short extra)
{
	if (win->handler) {
		win->handler(win, win->user_data, event, val, extra);
	}
}

static void window_free(Window *win) 
{
	MEM_freeN(win);
}

	/***/

static Window *active_gl_window= NULL;

Window *window_open(char *title, int posx, int posy, int sizex, int sizey, int start_maximized)
{
	GHOST_WindowHandle ghostwin;
	GHOST_TWindowState inital_state;
	int scr_w, scr_h;
    int i;

	winlay_get_screensize(&scr_w, &scr_h);
	posy= (scr_h-posy-sizey);
	
	if (start_maximized == G_WINDOWSTATE_FULLSCREEN)
		inital_state = start_maximized?GHOST_kWindowStateFullScreen:GHOST_kWindowStateNormal;
	else
		inital_state = start_maximized?GHOST_kWindowStateMaximized:GHOST_kWindowStateNormal;
#ifdef __APPLE__
	inital_state += macPrefState;
#endif

	ghostwin= GHOST_CreateWindow(g_system, 
								title, 
								posx, posy, sizex, sizey, 
								inital_state, 
								GHOST_kDrawingContextTypeOpenGL,
								0 /* no stereo */);
	
	if (ghostwin) {
		Window *win= window_new(ghostwin);
		
		if (win) {
			GHOST_SetWindowUserData(ghostwin, win);
			
			win->position[0]= posx;
			win->position[1]= posy;
			win->size[0]= sizex;
			win->size[1]= sizey;
			
			win->lmouse[0]= win->size[0]/2;
			win->lmouse[1]= win->size[1]/2;

            for (i = 0; i < 7; ++i)
                win->ndof[i] = 0;
		
			
		} else {
			GHOST_DisposeWindow(g_system, ghostwin);
		}
		
		return win;
	} else {
		return NULL;
	}
}

void window_set_handler(Window *win, WindowHandlerFP handler, void *user_data)
{
	win->handler= handler;
	win->user_data= user_data;
}

static void window_timer_proc(GHOST_TimerTaskHandle timer, GHOST_TUns64 time)
{
	Window *win= GHOST_GetTimerTaskUserData(timer);

	win->handler(win, win->user_data, win->timer_event, 0, 0);
}

void window_set_timer(Window *win, int delay_ms, int event)
{
	if (win->timer) GHOST_RemoveTimer(g_system, win->timer);

	win->timer_event= event;
	win->timer= GHOST_InstallTimer(g_system, delay_ms, delay_ms, window_timer_proc, win);
}

void window_destroy(Window *win) {
	if (active_gl_window==win) {
		active_gl_window= NULL;
	}
	
	if (win->timer) {
		GHOST_RemoveTimer(g_system, win->timer);
		win->timer= NULL;
	}

	GHOST_DisposeWindow(g_system, win->ghostwin);
	window_free(win);
}

void window_set_cursor(Window *win, int curs) {
	if (curs==CURSOR_NONE) {
		GHOST_SetCursorVisibility(win->ghostwin, 0);
	} else {
		GHOST_SetCursorVisibility(win->ghostwin, 1);
		
		/* detect if we use system cursor or Blender cursor */
		switch(curs) {
			case CURSOR_TEXTEDIT:
				SetBlenderCursor(BC_TEXTEDITCURSOR);
				break;
			case CURSOR_VPAINT:
				SetBlenderCursor(BC_PAINTBRUSHCURSOR);
				break;
			default:
				GHOST_SetCursorShape(win->ghostwin, convert_cursor(curs));
		}
	}
}

void window_set_custom_cursor(Window *win, unsigned char mask[16][2], 
					unsigned char bitmap[16][2], int hotx, int hoty) {
	GHOST_SetCustomCursorShape(win->ghostwin, bitmap, mask, hotx, hoty);
}

void window_set_custom_cursor_ex(Window *win, BCursor *cursor, int useBig) {
	if (useBig) {
		GHOST_SetCustomCursorShapeEx(win->ghostwin, 
			(GHOST_TUns8 *)cursor->big_bm, (GHOST_TUns8 *)cursor->big_mask, 
			cursor->big_sizex,cursor->big_sizey,
			cursor->big_hotx,cursor->big_hoty,
			cursor->fg_color, cursor->bg_color);
	} else {
		GHOST_SetCustomCursorShapeEx(win->ghostwin, 
			(GHOST_TUns8 *)cursor->small_bm, (GHOST_TUns8 *)cursor->small_mask, 
			cursor->small_sizex,cursor->small_sizey,
			cursor->small_hotx,cursor->small_hoty,
			cursor->fg_color, cursor->bg_color);
	}
}

void window_make_active(Window *win) {
	if (win != active_gl_window) {
		win->lmbut= 0;	/* keeps hanging when mousepressed while other window opened */
		active_gl_window= win;
		GHOST_ActivateWindowDrawingContext(win->ghostwin);
	}
}

void window_swap_buffers(Window *win) {
#ifdef _WIN32
	// adding a glFinish() here is to prevent Geforce in 'full scene antialias' mode
	// from antialising the Blender window. Officially a swapbuffers does a glFinish
	// itself, so this feels really like a hack... but it won't harm. (ton)
	// 
	// moved it here from ghost because it is a performance killer for the game engine,
	// glFinish forces synchronization with the graphics card and calling it is strongly
	// discouraged for good performance. (brecht)
	//
	glFinish();
#endif

	GHOST_SwapWindowBuffers(win->ghostwin);
}

static int query_qual(char qual) {
	GHOST_TModifierKeyMask left, right;
	int val= 0;
	
	if (qual=='s') {
		left= GHOST_kModifierKeyLeftShift;
		right= GHOST_kModifierKeyRightShift;
	} else if (qual=='c') {
		left= GHOST_kModifierKeyLeftControl;
		right= GHOST_kModifierKeyRightControl;
	} else if (qual=='C') {
		left= right= GHOST_kModifierKeyCommand;
	} else {
		left= GHOST_kModifierKeyLeftAlt;
		right= GHOST_kModifierKeyRightAlt;
	}

	GHOST_GetModifierKeyState(g_system, left, &val);
	if (!val)
		GHOST_GetModifierKeyState(g_system, right, &val);
	
	return val;
}

static int change_bit(int val, int bit, int to_on) {
	return to_on?(val|bit):(val&~bit);
}

static void update_tablet_data(Window *win, GHOST_WindowHandle ghostwin) {
	const GHOST_TabletData *td= GHOST_GetTabletData(ghostwin);
	
	/* if there's tablet data from an active tablet device then use it,
	 * otherwise set all tablet related data to default */
	if ((td != NULL) && ELEM(td->Active, DEV_STYLUS, DEV_ERASER)) {
		win->activedevice = (short)td->Active;
		win->pressure = td->Pressure;
		win->xtilt = td->Xtilt;
		win->ytilt = td->Ytilt;
	} else {
		win->activedevice = DEV_MOUSE;
		win->pressure = 1.0;
		win->xtilt = win->ytilt = 0.0;
	}
}


static int event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr private) 
{
	GHOST_TEventType type= GHOST_GetEventType(evt);

	if (type == GHOST_kEventQuit) {
		exit_usiblender();
	} else {
		GHOST_WindowHandle ghostwin= GHOST_GetEventWindow(evt);
		GHOST_TEventDataPtr data= GHOST_GetEventData(evt);
		Window *win;
		
		if (!ghostwin) {
			// XXX - should be checked, why are we getting an event here, and
			//	what is it?

			return 1;
		} else if (!GHOST_ValidWindow(g_system, ghostwin)) {
			// XXX - should be checked, why are we getting an event here, and
			//	what is it?

			return 1;
		} else {
			win= GHOST_GetWindowUserData(ghostwin);
		}
		
		switch (type) {

        case GHOST_kEventNDOFMotion: {
            // update ndof device data, and dispatch motion event

            GHOST_TEventNDOFData *sb= data;
			
				// no scaling per sfgoros patch
            win->ndof[0] = sb->tx;
            win->ndof[1] = sb->ty;
            win->ndof[2] = sb->tz;
            win->ndof[3] = sb->rx;
            win->ndof[4] = sb->ry;
            win->ndof[5] = sb->rz;
            win->ndof[6] = sb->delta;
 //        	printf(" motion capted %f %f %f %f %f %f %f \n", win->ndof[0], win->ndof[1], win->ndof[2],
 //        							 win->ndof[3], win->ndof[4], win->ndof[5], win->ndof[6]);


 //          window_handle(win, NDOFMOTION, win->ndof[6]);

 //       start interaction for larger than teeny-tiny motions
 //         if (fabs(win->ndof[0] > 0.003f) ||
 //             fabs(win->ndof[1] > 0.003f) ||
 //          fabs(win->ndof[2] > 0.003f) ||
 //               fabs(win->ndof[3] > 0.003f) ||
 //               fabs(win->ndof[4] > 0.003f) ||
 //              fabs(win->ndof[5] > 0.003f)) {
                    window_handle(win, NDOFMOTION, 1);
 //    printf("ok\n");
 //    }
;
          break;
        }
        case GHOST_kEventNDOFButton: {
			GHOST_TEventNDOFData *sb= data;
			
//			printf("this is a button %i\n", sb->buttons);
			window_handle(win, NDOFBUTTON, sb->buttons);
			break;
        }
		case GHOST_kEventCursorMove: {
			if(win->active == 1) {
				GHOST_TEventCursorData *cd= data;
				
				int cx, cy;
				
				GHOST_ScreenToClient(win->ghostwin, cd->x, cd->y, &cx, &cy);
				win->lmouse[0]= cx;
				win->lmouse[1]= (win->size[1]-1) - cy;
				
				update_tablet_data(win, ghostwin);			
				window_handle(win, MOUSEX, win->lmouse[0]);
				window_handle(win, MOUSEY, win->lmouse[1]);
			}
			break;
		}
		case GHOST_kEventButtonDown:
		case GHOST_kEventButtonUp: {
			GHOST_TEventButtonData *bd= data;
			int val= (type==GHOST_kEventButtonDown);
			int bbut= convert_mbut(bd->button);
		
			if (bbut==LEFTMOUSE) {
				if (val) {
					if (win->lqual & LR_COMMANDKEY) {
						bbut= win->faked_mbut= RIGHTMOUSE;
					} else if ((win->lqual & LR_ALTKEY) && (U.flag & USER_TWOBUTTONMOUSE)) {
						/* finally, it actually USES the userpref! :) -intrr */
						bbut= win->faked_mbut= MIDDLEMOUSE;
					}
				} else {
					if (win->faked_mbut) {
						bbut= win->faked_mbut;
						win->faked_mbut= 0;
					}
				}
			}

			if (bbut==LEFTMOUSE) {
				win->lmbut= change_bit(win->lmbut, L_MOUSE, val);
			} else if (bbut==MIDDLEMOUSE) {
				win->lmbut= change_bit(win->lmbut, M_MOUSE, val);
			} else {
				win->lmbut= change_bit(win->lmbut, R_MOUSE, val);
			}
			
			update_tablet_data(win, ghostwin);
			window_handle(win, bbut, val);
			
			break;
		}
	
		case GHOST_kEventKeyDown:
		case GHOST_kEventKeyUp: {
			GHOST_TEventKeyData *kd= data;
			int val= (type==GHOST_kEventKeyDown);
			int bkey= convert_key(kd->key);

			if (bkey) {
				if (bkey==LEFTSHIFTKEY || bkey==RIGHTSHIFTKEY) {
					win->lqual= change_bit(win->lqual, LR_SHIFTKEY, val);
				} else if (bkey==LEFTCTRLKEY || bkey==RIGHTCTRLKEY) {
					win->lqual= change_bit(win->lqual, LR_CTRLKEY, val);
				} else if (bkey==LEFTALTKEY || bkey==RIGHTALTKEY) {
					win->lqual= change_bit(win->lqual, LR_ALTKEY, val);
				} else if (bkey==COMMANDKEY) {
					win->lqual= change_bit(win->lqual, LR_COMMANDKEY, val);
				}

				window_handle_ext(win, bkey, val, kd->ascii);
			}
			
			break;
		}

		case GHOST_kEventWheel:	{
			GHOST_TEventWheelData* wheelData = (GHOST_TEventWheelData*) data;
			if (wheelData->z > 0) {
				window_handle(win, WHEELUPMOUSE, 1);
			} else {
				window_handle(win, WHEELDOWNMOUSE, 1);
			}
			break;
		}

		case GHOST_kEventWindowDeactivate:
		case GHOST_kEventWindowActivate: {
			win->active= (type==GHOST_kEventWindowActivate);
			window_handle(win, INPUTCHANGE, win->active);
			
			if (win->active) {
				if ((win->lqual & LR_SHIFTKEY) && !query_qual('s')) {
					win->lqual= change_bit(win->lqual, LR_SHIFTKEY, 0);
					window_handle(win, LEFTSHIFTKEY, 0);
				}
				if ((win->lqual & LR_CTRLKEY) && !query_qual('c')) {
					win->lqual= change_bit(win->lqual, LR_CTRLKEY, 0);
					window_handle(win, LEFTCTRLKEY, 0);
				}
				if ((win->lqual & LR_ALTKEY) && !query_qual('a')) {
					win->lqual= change_bit(win->lqual, LR_ALTKEY, 0);
					window_handle(win, LEFTALTKEY, 0);
				}
				if ((win->lqual & LR_COMMANDKEY) && !query_qual('C')) {
					win->lqual= change_bit(win->lqual, LR_COMMANDKEY, 0);
					window_handle(win, LR_COMMANDKEY, 0);
				}

				/* 
				 * XXX quick hack so OSX version works better
				 * when the window is clicked on (focused).
				 *
				 * it used to pass on the old win->lmouse value,
				 * which causes a wrong click in Blender.
				 * Actually, a 'focus' click should not be passed
				 * on to blender... (ton)
				 */
				if(1) { /* enables me to add locals */
					int cx, cy, wx, wy;
					GHOST_GetCursorPosition(g_system, &wx, &wy);

					GHOST_ScreenToClient(win->ghostwin, wx, wy, &cx, &cy);
					win->lmouse[0]= cx;
					win->lmouse[1]= (win->size[1]-1) - cy;
					window_handle(win, MOUSEX, win->lmouse[0]);
					window_handle(win, MOUSEY, win->lmouse[1]);
				}
			}
			
			break;
		}
		case GHOST_kEventWindowClose: {
			window_handle(win, WINCLOSE, 1);
			break;
		}
		case GHOST_kEventWindowUpdate: {
			window_handle(win, REDRAW, 1);
			break;
		}
		case GHOST_kEventWindowSize: {
			GHOST_RectangleHandle client_rect;
			int l, t, r, b, scr_w, scr_h;

			client_rect= GHOST_GetClientBounds(win->ghostwin);
			GHOST_GetRectangle(client_rect, &l, &t, &r, &b);
			
			GHOST_DisposeRectangle(client_rect);
			
			winlay_get_screensize(&scr_w, &scr_h);
			win->position[0]= l;
			win->position[1]= scr_h - b - 1;
			win->size[0]= r-l;
			win->size[1]= b-t;

			window_handle(win, RESHAPE, 1);
			break;
		}
		case GHOST_kEventUnknown:
		case GHOST_kEventQuit:
		case GHOST_kNumEventTypes:
			break;
	}
	}
	
	return 1;
}

void window_get_ndof(Window* win, float* sbval) {
    int i;
    for (i = 0; i < 7; ++i) {
        *sbval++ = win->ndof[i];
    }
}

char *window_get_title(Window *win) {
	char *title= GHOST_GetTitle(win->ghostwin);
	char *mem_title= BLI_strdup(title);
	free(title);

	return mem_title;
}

void window_set_title(Window *win, char *title) {
	GHOST_SetTitle(win->ghostwin, title);
}

short window_get_qual(Window *win) 
{
	int qual= 0;
	
	if( query_qual('s')) qual |= LR_SHIFTKEY;
	if( query_qual('a')) qual |= LR_ALTKEY;
	if( query_qual('c')) qual |= LR_CTRLKEY;
	return qual;
//	return win->lqual;
}

short window_get_mbut(Window *win) {
	return win->lmbut;
}

void window_get_mouse(Window *win, short *mval) {
	mval[0]= win->lmouse[0];
	mval[1]= win->lmouse[1];
}

float window_get_pressure(Window *win) {
	return win->pressure;
}

void window_get_tilt(Window *win, float *xtilt, float *ytilt) {
	*xtilt= win->xtilt;
	*ytilt= win->ytilt;
}

short window_get_activedevice(Window *win) {
	return win->activedevice;
}

void window_get_position(Window *win, int *posx_r, int *posy_r) {
	*posx_r= win->position[0];
	*posy_r= win->position[1];
}

void window_get_size(Window *win, int *width_r, int *height_r) {
	*width_r= win->size[0];
	*height_r= win->size[1];
}

void window_set_size(Window *win, int width, int height) {
	GHOST_SetClientSize(win->ghostwin, width, height);
}

void window_lower(Window *win) {
	GHOST_SetWindowOrder(win->ghostwin, GHOST_kWindowOrderBottom);
}

void window_raise(Window *win) {
	GHOST_SetWindowOrder(win->ghostwin, GHOST_kWindowOrderTop);
#ifdef _WIN32
	markdirty_all(); /* to avoid redraw errors in fullscreen mode (aphex) */
#endif
}

#if 0
//#ifdef _WIN32	//FULLSCREEN
void window_toggle_fullscreen(Window *win, int fullscreen) {
	/* these two lines make sure front and backbuffer are equal. for swapbuffers */
	markdirty_all();
	screen_swapbuffers();

	if(fullscreen)
		GHOST_SetWindowState(win->ghostwin, GHOST_kWindowStateFullScreen);
	else
		GHOST_SetWindowState(win->ghostwin, GHOST_kWindowStateMaximized);
}
#endif

void window_warp_pointer(Window *win, int x, int y) {
	int oldx=x, oldy=y;
	
	y= win->size[1] - y - 1;
	GHOST_ClientToScreen(win->ghostwin, x, y, &x, &y);
	GHOST_SetCursorPosition(g_system, x, y);
	
	/* on OSX (for example) the setcursor doesnt create event */
	win->lmouse[0]= oldx;
	win->lmouse[1]= oldy;
}

void window_queue_redraw(Window *win) {
	GHOST_InvalidateWindow(win->ghostwin); // ghost will send back a redraw to blender
}

/***/

void winlay_process_events(int wait_for_event) {
	GHOST_ProcessEvents(g_system, wait_for_event);
	GHOST_DispatchEvents(g_system);
}

void winlay_get_screensize(int *width_r, int *height_r) {
	unsigned int uiwidth;
	unsigned int uiheight;
	
	if (!g_system) {
		GHOST_EventConsumerHandle consumer= GHOST_CreateEventConsumer(event_proc, NULL);
	
		g_system= GHOST_CreateSystem();
		GHOST_AddEventConsumer(g_system, consumer);
	}
	
	GHOST_GetMainDisplayDimensions(g_system, &uiwidth, &uiheight);
	*width_r= uiwidth;
	*height_r= uiheight;
}

Window *winlay_get_active_window(void) {
	return active_gl_window;
}

#ifdef _WIN32
#define PATH_SEP		"\\"
#else
#define PATH_SEP		"/"
#endif


void window_open_ndof(Window* win)
{
	char *inst_path, *plug_path;
	const char *plug_dir = "plugins";
	const char *plug_name = "3DxNdofBlender.plug";
	PILdynlib *ndofLib;
	
	// build the plugin path
	plug_path = NULL;
	inst_path = get_install_dir(); // path to main blender exec/bundle
	if (inst_path) {
		// assume the ndof plugin is located in the plug-in dir
		size_t len = strlen(inst_path) + strlen(plug_dir) + strlen(PATH_SEP)*2
		             + strlen(plug_name) + 1;
		plug_path = MEM_mallocN(len, "ndofpluginpath");
		if (plug_path) {
			strncpy(plug_path, inst_path, len);
			strcat(plug_path, PATH_SEP);
			strcat(plug_path, plug_dir);
			strcat(plug_path, PATH_SEP);
			strcat(plug_path, plug_name);
		}
		MEM_freeN(inst_path);
	}
	
	ndofLib	= PIL_dynlib_open(plug_path);
	
	/* On systems where blender is installed in /usr/bin/blender, ~/.blender/plugins/ is a better place to look */
	if (ndofLib==NULL) {
		
		if (plug_path) {
			MEM_freeN(plug_path);
		}
		
		inst_path = BLI_gethome();
		if (inst_path) {
			size_t len = strlen(inst_path) + strlen(plug_dir) + strlen(PATH_SEP)*2
					 + strlen(plug_name) + 1;
			
			if (!strstr(inst_path, ".blender")) {
				len += strlen(".blender") + strlen(PATH_SEP);
			}
			
			plug_path = MEM_mallocN(len, "ndofpluginpath");
			if (plug_path) {
				strncpy(plug_path, inst_path, len);
				strcat(plug_path, PATH_SEP);
				if (!strstr(inst_path, ".blender")) {
					strcat(plug_path, ".blender");
					strcat(plug_path, PATH_SEP);
				}
				strcat(plug_path, plug_dir);
				strcat(plug_path, PATH_SEP);
				strcat(plug_path, plug_name);
			}
		}
		
		ndofLib	= PIL_dynlib_open(plug_path);
	}
	
	
	
#if 0
	fprintf(stderr, "plugin path=%s; ndofLib=%p\n", plug_path, (void*)ndofLib);
#endif
	
	if (plug_path)
		MEM_freeN(plug_path);
	
	if (ndofLib) {
		G.ndofdevice = 0 - GHOST_OpenNDOF(g_system, win->ghostwin, 
		               PIL_dynlib_find_symbol(ndofLib, "ndofInit"),
		               PIL_dynlib_find_symbol(ndofLib, "ndofShutdown"),
		               PIL_dynlib_find_symbol(ndofLib, "ndofOpen"));
		
		}
    else {
        GHOST_OpenNDOF(g_system, win->ghostwin, 0, 0, 0);
        G.ndofdevice = -1;
    }
 }

char *getClipboard(int flag) {
	return (char*)GHOST_getClipboard(flag);
}

void putClipboard(char *buffer, int flag) {
	GHOST_putClipboard((GHOST_TInt8*)buffer, flag);
}
